using System;
using System.Collections.Generic;
using System.Xml;
using System.Net;
using System.Diagnostics;
using TALoaders;
using TAAlert.CommonUtils;

namespace TAAlert.Alerts
{
    class TwoEmaAlert : Alert
    {
        private static readonly string N_SHORT_XML = "nEmaShort";
        private static readonly string N_LONG_XML = "nEmaLong";
        private static readonly string VOL_TREASHOLD = "volTreashold";

        private double emaShort0M;  // Yesterday's EmaShort
        private double emaLong0M;   // Yesterday's EmaLong
        private double price0M;     // Yesterday's price
        private double vol0M;       // Yeasterday's volatility
        private int nEmaShortM;     // EMAShort period
        private int nEmaLongM;      // EMASLong period
        private double volTM;       // treashold volatility
        private DateTime lastHistDateM; // the last date of the historical prices

        /// <summary>TwoEMAAlert constructor used with any External Data loaders</summary>
        /// <param name="loader">Any base loader</param>
        /// <param name="ticker">Security ticker</param>
        /// <param name="nEmaShort">short ema period</param>
        /// <param name="nEmaLong">long ema period</param>
        /// <param name="volTreashold">volatility treashold</param>
        public TwoEmaAlert(BaseLoader loader, string ticker, int nEmaShort, int nEmaLong, double volTreashold)
            : base(loader, ticker, StrategyName.TwoEMA)
        {
            this.nEmaShortM = nEmaShort;
            this.nEmaLongM = nEmaLong;
            this.volTM = volTreashold;
            this.setUpPrivateMembers();
        }

        /// <summary>Construct TwoEMA alert from from Xml-code</summary>
        /// <param name="r">XMLReader with open TwoEMA node</param>
        public TwoEmaAlert(XmlReader r)
            : base(r, StrategyName.TwoEMA)
        {
            this.nEmaShortM = r.ReadElementContentAsInt(N_SHORT_XML, "");
            this.nEmaLongM = r.ReadElementContentAsInt(N_LONG_XML, "");
            this.volTM = r.ReadElementContentAsDouble(VOL_TREASHOLD, "");

            r.ReadEndElement();

            this.setUpPrivateMembers();
        }

        private void setUpPrivateMembers()
        {
            DateTime end = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToShortDateString()); // trunc all time information
            DateTime start = end.AddDays(-7 * Math.Max(nEmaShortM, nEmaLongM));

            SortedList<DateTime, double> hPrice;
            try
            {
                hPrice = loaderM.getHistoricalPrices(this.Ticker, start, end, QuoteType.AdjClose);
            }
            catch (WebException wex)
            {
                throw new WebException("TwoEMAAlert: Cannot load ticker: '" + base.Ticker + "' using loader:" + base.loaderM.Loader + "\n" + wex.Message);
            }

            List<double> hEmaShort = EMAEvaluator.calcEMAArray(hPrice.Values, nEmaShortM);
            List<double> hEmaLong = EMAEvaluator.calcEMAArray(hPrice.Values, nEmaLongM);
            List<double> hVol = EMAEvaluator.calcEMAVolatilityArray(hPrice.Values);
            Debug.Assert(hVol.Count == hEmaLong.Count && hEmaShort.Count == hPrice.Count, "TwoEMA: array size missmatch");

            int n = hPrice.Count;
            this.price0M = hPrice.Values[n - 1];
            this.vol0M = hVol[n - 1];
            emaShort0M = hEmaShort[n - 1];
            emaLong0M = hEmaLong[n - 1];
            lastHistDateM = hPrice.Keys[n - 1];
        }

        /// <summary>Single line Alert description</summary>
        public override string Description
        {
            get
            {
                return string.Format("{0,-8}: nEmaShort={1,-8} nEmaLong={2,-8} volT={3:P1}", base.XmlName, this.nEmaShortM, this.nEmaLongM, this.volTM);
            }
        }

        /// <summary>Return latest signal value based on the last trade price and historical data</summary>
        /// <param name="info">OUT: additional information</param>
        /// <returns>BUY/SELL/NEUTRAL signal</returns>
        public override Signal evalAlert(out string[] info)
        {
            string timeStamp;
            double price1 = loaderM.getLastPrice(this.Ticker, out timeStamp);
            double emaShort1 = EMAEvaluator.calcEMA(emaShort0M, nEmaShortM, price1);
            double emaLong1 = EMAEvaluator.calcEMA(emaLong0M, nEmaLongM, price1);
            double vol1 = EMAEvaluator.calcEMAVolatility(this.vol0M, this.price0M, price1); 

            info = new string[9];
            info[0] = string.Format("Price = {0:.####}      (as of {1})", price1, timeStamp);
            info[1] = string.Format("EMA({0}) = {1:.####}", nEmaShortM, emaShort1);
            info[2] = string.Format("EMA({0}) = {1:.####}", nEmaLongM, emaLong1);
            info[3] = string.Format("Volatility = {0:P1}", vol1);
            info[4] = "Old Signal = " + this.oldAlert();
            info[5] = string.Format("Old Price = {0:.####}   (as of {1})", price0M, lastHistDateM.ToShortDateString());
            info[6] = string.Format("Old EMA({0}) = {1:.####}", nEmaShortM, emaShort0M);
            info[7] = string.Format("Old EMA({0}) = {1:.####}", nEmaLongM, emaLong0M);
            info[8] = string.Format("Old Volatility = {0:P1}", this.vol0M);

            return EMAEvaluator.emaVolCutSignal(emaShort1, emaLong1, vol1, this.volTM);
        }

        // Yesterday's signal value
        public override Signal oldAlert()
        {
            return EMAEvaluator.emaVolCutSignal(this.emaShort0M, this.emaLong0M, this.vol0M, this.volTM);
        }

        public override void writeXml(XmlWriter writer)
        {
            base.writeXml(writer);
            writer.WriteElementString(N_SHORT_XML, this.nEmaShortM.ToString());
            writer.WriteElementString(N_LONG_XML, this.nEmaLongM.ToString());
            writer.WriteElementString(VOL_TREASHOLD, this.volTM.ToString());
        }
    }
}
